package com.neuronbit.businessflow.intellij.lang;

import com.intellij.lang.ASTNode;
import com.intellij.lang.PsiParser;
import com.intellij.lexer.Lexer;
import com.intellij.openapi.project.Project;
import com.intellij.psi.FileViewProvider;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.IFileElementType;
import com.intellij.psi.tree.TokenSet;
import com.neuronbit.businessflow.intellij.BpmLanguage;
import com.neuronbit.businessflow.intellij.antlr.BpmLexer;
import com.neuronbit.businessflow.intellij.antlr.BpmParser;
import org.antlr.intellij.adaptor.lexer.ANTLRLexerAdaptor;
import org.antlr.intellij.adaptor.lexer.PSIElementTypeFactory;
import org.antlr.intellij.adaptor.lexer.RuleIElementType;
import org.antlr.intellij.adaptor.lexer.TokenIElementType;
import org.antlr.intellij.adaptor.parser.ANTLRParserAdaptor;
import org.antlr.intellij.adaptor.psi.ANTLRPsiNode;
import org.antlr.v4.runtime.Parser;
import org.antlr.v4.runtime.tree.ParseTree;
import org.jetbrains.annotations.NotNull;

import java.util.List;

/**
 * The type Bpm parser definition.
 */
public class BpmParserDefinition implements com.intellij.lang.ParserDefinition {

    static {
        PSIElementTypeFactory.defineLanguageIElementTypes(BpmLanguage.INSTANCE, BpmParser.VOCABULARY, BpmParser.ruleNames);
        //CALL = tokenIElementTypes.get(BpmParser.CALL);
    }

    /**
     * The constant tokens.
     */
    public static final List<TokenIElementType> tokens = PSIElementTypeFactory.getTokenIElementTypes(BpmLanguage.INSTANCE);
    /**
     * The constant rules.
     */
    public static final List<RuleIElementType> rules = PSIElementTypeFactory.getRuleIElementTypes(BpmLanguage.INSTANCE);

    /**
     * The constant FILE.
     */
    public static final IFileElementType FILE = new IFileElementType(BpmLanguage.INSTANCE);

    /**
     * The constant COMMENTS.
     */
    public static final TokenSet COMMENTS = PSIElementTypeFactory.createTokenSet(
            BpmLanguage.INSTANCE, BpmLexer.MULTI_LINE_COMMENT, BpmLexer.SINGLE_LINE_COMMENT);
    /**
     * The constant WHITESPACE.
     */
    public static final TokenSet WHITESPACE = PSIElementTypeFactory.createTokenSet(
            BpmLanguage.INSTANCE, BpmLexer.SPACE);

    /**
     * The constant STRING.
     */
    public static final TokenSet STRING = PSIElementTypeFactory.createTokenSet(
            BpmLanguage.INSTANCE, BpmLexer.STRING
    );

    /**
     * The constant ATTRIBUTE.
     */
    public static final TokenSet ATTRIBUTE = PSIElementTypeFactory.createRuleTokenSet(
            BpmLanguage.INSTANCE, BpmParser.RULE_attribute
    );

    /**
     * The constant PARAM.
     */
    public static final TokenSet PARAM = PSIElementTypeFactory.createRuleTokenSet(
            BpmLanguage.INSTANCE, BpmParser.RULE_param
    );

//    public static final TokenSet CALL = PSIElementTypeFactory.createTokenSet(
//            BpmLanguage.INSTANCE, BpmLexer.CALL
//    );


//    public static TokenIElementType CALL;

    @Override
    public @NotNull Lexer createLexer(Project project) {
        BpmLexer bpmLexer = new BpmLexer(null);
        return new ANTLRLexerAdaptor(BpmLanguage.INSTANCE, bpmLexer);
    }

    @Override
    public @NotNull PsiParser createParser(Project project) {
        BpmParser parser = new BpmParser(null);
        //BpmLexer lexer = new BpmLexer(null);
        return new ANTLRParserAdaptor(BpmLanguage.INSTANCE, parser) {
            @Override
            protected ParseTree parse(Parser parser, IElementType root) {
                // start rule depends on root passed in; sometimes we want to create an ID node etc...
                try {
                    if (root instanceof IFileElementType) {
                        return ((BpmParser) parser).model();
                    }
                    //TODO shunyun 2023/2/17:
                    // let's hope it's an ID as needed by "rename function"
                    return ((BpmParser) parser).model();
                } catch (Exception e) {
                    e.printStackTrace();
                    //ignore
                }
                return null;
            }
        };
    }

    @Override
    public @NotNull TokenSet getWhitespaceTokens() {
        return WHITESPACE;
    }

    @Override
    public @NotNull IFileElementType getFileNodeType() {
        return FILE;
    }

    @Override
    public @NotNull TokenSet getCommentTokens() {
        return COMMENTS;
    }

    @Override
    public @NotNull TokenSet getStringLiteralElements() {
        return STRING;
    }

    @Override
    public @NotNull PsiElement createElement(ASTNode node) {
        IElementType elementType = node.getElementType();
        if (elementType == rules.get(BpmParser.RULE_callDef)) {
            return new PsiJavaMethodCall(node);
        } else if (elementType == rules.get(BpmParser.RULE_attribute)) {
            return new PsiAttribute(node);
        } else if (elementType == rules.get(BpmParser.RULE_paramArray)) {
            return new PsiParamArray(node);
        } else if (elementType == rules.get(BpmParser.RULE_param)) {
            return new PsiParam(node);
        } else {
            return new ANTLRPsiNode(node);
        }
    }

    @Override
    public @NotNull PsiFile createFile(@NotNull FileViewProvider fileViewProvider) {
        return new PsiBpmFile(fileViewProvider);
    }
}
